/**
  ******************************************************************************
  * @author     Chris
  * @since      2024/6/18 17:31
  *
  * @file       pd_adc.c
  * @brief      ADC peripheral driver.
  *
  * @note       This file contains the peripheral driver for the ADC.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date          Author       Notes
  *   2024/6/18     Chris        the first version
  *
  ******************************************************************************
  */

#include <stm32f4xx_ll_adc.h>
#include <stm32f4xx_ll_bus.h>
#include <stm32f4xx_ll_gpio.h>
#include "pd_adc.h"

static void ADCBase_init(ADCBase *this) {
    // Set ADC Clock
    if      (this->ADCx == ADC1) LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC1);
    else if (this->ADCx == ADC2) LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC2);
    else if (this->ADCx == ADC3) LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC3);

    // GPIO init
    if (this->adcChannel->gpioSize > 0) {
        GPIO **gpio = this->adcChannel->gpios;
        for (int i = 0; i < this->adcChannel->gpioSize; ++i) {
            gpio[i]->Mode  = LL_GPIO_MODE_ANALOG;
            gpio[i]->Speed = LL_GPIO_SPEED_FREQ_HIGH;
            gpio[i]->Pull  = LL_GPIO_PULL_NO;
            gpio[i]->init(gpio[i]);
        }
    }

    // ADC init
    LL_ADC_InitTypeDef ADC_InitStruct = {0};
    LL_ADC_StructInit(&ADC_InitStruct);
    ADC_InitStruct.Resolution         = this->Resolution;                   // ADC Resolution: 6, 8, 12 bit available
    ADC_InitStruct.DataAlignment      = this->DataAlignment;             // ADC data alignment: left, right
    ADC_InitStruct.SequencersScanMode = this->SequencersScanMode;   // ADC scan mode(ADC_CR1_SCAN,Bit 8 SCAN: Scan mode):
    LL_ADC_Init(this->ADCx, &ADC_InitStruct);

    // ADC Common init
    LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
    LL_ADC_CommonStructInit(&ADC_CommonInitStruct);
    ADC_CommonInitStruct.CommonClock           = this->CommonClock;           // ADC clock: AHB clock with prescaler division by 2/4/6/8
#if defined(ADC_MULTIMODE_SUPPORT)                                  // Multi mode
    ADC_CommonInitStruct.Multimode             = this->Multimode;
    ADC_CommonInitStruct.MultiDMATransfer      = this->MultiDMATransfer;
    ADC_CommonInitStruct.MultiTwoSamplingDelay = this->MultiTwoSamplingDelay;
#endif
    LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(this->ADCx), &ADC_CommonInitStruct);

}

static ADCBase *build(ADC_TypeDef *ADCx, ADCCh *adcChannel, uint32_t SequencersScanMode) {
    ADCBase *gpio = malloc(sizeof(ADCBase));

    Driver_add(gpio, GEN_PERM);

    gpio->ADCx = ADCx;
    gpio->adcChannel            = adcChannel;
    gpio->Resolution            = LL_ADC_RESOLUTION_12B;
    gpio->DataAlignment         = LL_ADC_DATA_ALIGN_RIGHT;
    gpio->SequencersScanMode    = SequencersScanMode;
    gpio->CommonClock           = LL_ADC_CLOCK_SYNC_PCLK_DIV4;
#if defined(ADC_MULTIMODE_SUPPORT)
    gpio->Multimode             = LL_ADC_MULTI_INDEPENDENT;
    gpio->MultiDMATransfer      = LL_ADC_MULTI_REG_DMA_EACH_ADC;
    gpio->MultiTwoSamplingDelay = LL_ADC_MULTI_TWOSMP_DELAY_5CYCLES;
#endif /* ADC_MULTIMODE_SUPPORT */

    gpio->init = &ADCBase_init;

    return gpio;
}

const struct ADCBaseClass ADCBaseBuilder = {.build = &build};
